package cn.yangliu.task.dao;

import cn.yangliu.task.config.TaskProperties;
import cn.yangliu.task.entity.PageInfo;
import cn.yangliu.task.entity.Task;
import cn.yangliu.task.enums.TaskExecuteStatusEnum;
import cn.yangliu.task.enums.TaskStatusEnum;
import cn.yangliu.task.utils.IdUtils;
import cn.yangliu.task.utils.PageThreadLocal;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.sql.DataSource;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.List;

public class TaskDao {

    private JdbcTemplate jdbcTemplate;

    private String tableName = "task";

    @Resource
    private TaskProperties properties;

    public void setTableName(String tableName) {
        this.tableName = tableName;
    }

    @Resource
    public void setDataSource(DataSource dataSource) {
        if (dataSource != null) {
            jdbcTemplate = new JdbcTemplate(dataSource);
            jdbcTemplate.setDataSource(dataSource);
        }
    }

    private RowMapper<Task> rowMapper = (ResultSet resultSet, int index) -> {
        Task task = new Task();
        task.setId(resultSet.getString("id"));
        task.setRemark(resultSet.getString("remark"));
        Timestamp create_time = resultSet.getTimestamp("create_time");
        Timestamp update_time = resultSet.getTimestamp("update_time");
        if (create_time != null) {
            LocalDateTime createLocalDateTime = LocalDateTime.ofInstant(create_time.toInstant(), ZoneId.systemDefault());
            task.setCreateTime(createLocalDateTime);
        }
        if (update_time != null) {
            LocalDateTime updateLocalDateTime = LocalDateTime.ofInstant(update_time.toInstant(), ZoneId.systemDefault());
            task.setUpdateTime(updateLocalDateTime);
        }
        task.setName(resultSet.getString("name"));
        task.setClazz(resultSet.getString("clazz"));
        task.setMethod(resultSet.getString("method"));
        task.setExcuteCount(resultSet.getInt("excute_count"));
        task.setExcuteTotalCount(resultSet.getInt("excute_total_count"));
        task.setStatus(resultSet.getInt("status"));
        task.setExcuteStatus(resultSet.getInt("excute_Status"));
        task.setCron(resultSet.getString("cron"));
        task.setConfig(resultSet.getString("config"));
        return task;
    };

    public List<Task> queryAllList() {
        return jdbcTemplate.query("select * from " + tableName + " where status = " + TaskStatusEnum.ACTIVE.getStatus(), rowMapper);
    }

    public void update(Task task) {
        StringBuilder sb = new StringBuilder("update task set ");
        List<Object> args = new ArrayList<>();
        if (!StringUtils.isEmpty(task.getClazz())) {
            sb.append("clazz=?,");
            args.add(task.getClazz());
        }

        if (!StringUtils.isEmpty(task.getCron())) {
            sb.append("cron=?,");
            args.add(task.getCron());
        }

        if (!StringUtils.isEmpty(task.getMethod())) {
            sb.append("method=?,");
            args.add(task.getMethod());
        }
        if (!StringUtils.isEmpty(task.getName())) {
            sb.append("name=?,");
            args.add(task.getName());
        }
        if (task.getStatus() != null) {
            sb.append("status=?,");
            args.add(task.getStatus());
        }

        if (task.getExcuteTotalCount() != null) {
            sb.append("excute_total_count=?,");
            args.add(task.getExcuteTotalCount());
        }

        if (task.getExcuteStatus() != null) {
            sb.append("excute_status=?,");
            args.add(task.getExcuteStatus());
        }

        if (!StringUtils.isEmpty(task.getRemark())) {
            sb.append("remark=?,");
            args.add(task.getRemark());
        }

        if (!StringUtils.isEmpty(task.getConfig())) {
            sb.append("config=?,");
            args.add(task.getConfig());
        }

        sb.append("update_time=?");
        args.add(LocalDateTime.now());

        if (StringUtils.isEmpty(task.getId())) {
            throw new NullPointerException("task缺少唯一主键");
        }
        sb.append(" where id = ?");
        args.add(task.getId());

        jdbcTemplate.update(sb.toString(), args.toArray());
    }


    public void insert(Task task) {
        StringBuilder sb = new StringBuilder("insert into " + tableName);
        StringBuilder columnSQL = new StringBuilder();
        List<Object> args = new ArrayList<>();
        if (!StringUtils.isEmpty(task.getClazz())) {
            columnSQL.append("clazz,");
            args.add(task.getClazz());
        }

        if (!StringUtils.isEmpty(task.getCron())) {
            columnSQL.append("cron,");
            args.add(task.getCron());
        }

        if (task.getStatus() == null) {
            task.setStatus(TaskStatusEnum.ACTIVE.getStatus());
        }
        columnSQL.append("status,");
        args.add(task.getStatus());


        if (task.getExcuteStatus() == null) {
            task.setExcuteStatus(TaskExecuteStatusEnum.INIT.getStatus());
        }

        if (task.getExcuteTotalCount() != null) {
            columnSQL.append("excute_total_count,excute_count,");
            args.add(task.getExcuteTotalCount());
            args.add(0);
        }

        columnSQL.append("excute_status,");
        args.add(task.getExcuteStatus());

        if (!StringUtils.isEmpty(task.getMethod())) {
            columnSQL.append("method,");
            args.add(task.getMethod());
        }
        if (!StringUtils.isEmpty(task.getName())) {
            columnSQL.append("name,");
            args.add(task.getName());
        }

        if (!StringUtils.isEmpty(task.getConfig())) {
            columnSQL.append("config,");
            args.add(task.getConfig());
        }

        columnSQL.append("create_time,");
        args.add(LocalDateTime.now());

        columnSQL.append("id");
        String id = task.getId();
        if (StringUtils.isEmpty(id)) {
            id = IdUtils.createID();
            task.setId(id);
        }

        args.add(id);

        sb.append("(").append(columnSQL.toString()).append(")").append(" values(");
        for (int i = 0; i < args.size(); i++) {
            if (i == args.size() - 1) {
                sb.append("?)");
            } else {
                sb.append("?,");
            }
        }
        jdbcTemplate.update(sb.toString(), args.toArray());
    }


    public void delete(String id) {
        jdbcTemplate.update("delete from task where id = ?", new Object[]{id});
    }

    public Task getOne(Task queryPojo) {
        StringBuilder sb = new StringBuilder("select * from " + tableName + " where 1=1 ");
        List<Object> args = new ArrayList<>();
        if (!StringUtils.isEmpty(queryPojo.getClazz())) {
            sb.append("and clazz=? ");
            args.add(queryPojo.getClazz());
        }

        if (!StringUtils.isEmpty(queryPojo.getCron())) {
            sb.append("and cron=? ");
            args.add(queryPojo.getCron());
        }

        if (!StringUtils.isEmpty(queryPojo.getMethod())) {
            sb.append("and method=? ");
            args.add(queryPojo.getMethod());
        }
        if (!StringUtils.isEmpty(queryPojo.getName())) {
            sb.append("and name=? ");
            args.add(queryPojo.getName());
        }
        if (queryPojo.getQueryStartDate() != null) {
            sb.append("and create_time>=? ");
            args.add(queryPojo.getQueryStartDate());
        }

        if (queryPojo.getQueryEndDate() != null) {
            sb.append("and create_time<=? ");
            args.add(queryPojo.getQueryEndDate());
        }

        if (!StringUtils.isEmpty(queryPojo.getId())) {
            sb.append("and id = ?");
            args.add(queryPojo.getId());
        }

        return jdbcTemplate.queryForObject(sb.toString(), rowMapper, args.toArray());
    }

    public List<Task> list(Task queryPojo) {
        StringBuilder sql = new StringBuilder("select * from " + tableName + " where 1=1 ");
        List<Object> args = new ArrayList<>();
        if (!StringUtils.isEmpty(queryPojo.getCondition())) {
            sql.append("and ( clazz=? ");
            args.add(queryPojo.getCondition());
            sql.append("or cron=? ");
            args.add(queryPojo.getCondition());
            sql.append("or method=? ");
            args.add(queryPojo.getCondition());
            sql.append("or name=? )");
            args.add(queryPojo.getCondition());
        }

        if (queryPojo.getStatus() != null) {
            sql.append("and status=? ");
            args.add(queryPojo.getStatus());
        }

        if (queryPojo.getExcuteStatus() != null) {
            sql.append("and excute_status=? ");
            args.add(queryPojo.getExcuteStatus());
        }

        if (queryPojo.getQueryStartDate() != null) {
            sql.append("and create_time>=? ");
            args.add(queryPojo.getQueryStartDate());
        }

        if (queryPojo.getQueryEndDate() != null) {
            sql.append("and create_time<=? ");
            args.add(queryPojo.getQueryEndDate());
        }

        int pageSize = queryPojo.getPageSize();

        int currentPage = queryPojo.getCurrentPage();

        String excuteSQL = sql.toString();

        if (pageSize != 0 && currentPage != 0) {
            int start = (currentPage - 1) * pageSize;

            String countSQL = "select count(*) from (" + sql.toString() + ") as count_temp";

            int total = jdbcTemplate.queryForObject(countSQL, args.toArray(), Integer.class);

            int pages = (total - 1) / pageSize + 1;
            PageInfo pageInfo = new PageInfo(currentPage, pageSize, start, total, pages);

            PageThreadLocal.setPageInfo(pageInfo);

            String mysqlSQL = sql.toString() + " limit " + start + "," + pageSize;
            String oracleSQL = "select * from (select tmp_tb.*,ROWNUM row_id from ( "
                    + sql.toString()
                    + " ) as tmp_tb where ROWNUM<= "
                    + (start + pageSize)
                    + " ) where row_id> "
                    + start;

            if ("mysql".equalsIgnoreCase(properties.getDialect())){
                excuteSQL = mysqlSQL;
            }else {
                excuteSQL = oracleSQL;
            }
        }

        return jdbcTemplate.query(excuteSQL, args.toArray(), rowMapper);
    }

    /**
     * 将活动状态的任务执行状态重置
     */
    public void resetTask() {
        String sql = "update " + tableName + " set excute_status = ? where status = ?";
        jdbcTemplate.update(sql, TaskExecuteStatusEnum.INIT.getStatus(), TaskStatusEnum.ACTIVE.getStatus());
    }


    /**
     * 更新任务执行次数
     *
     * @param task
     */
    public void updateExcuteCount(Task task) {
        String sql = "update " + tableName + " set excute_count =(excute_count+1) where id = ?";
        jdbcTemplate.update(sql, task.getId());
    }
}
